repo: Add an API to read and parse directory metadata
authorColin Walters <walters@verbum.org>
Wed, 15 Dec 2021 18:57:54 +0000 (13:57 -0500)
committerColin Walters <walters@verbum.org>
Fri, 6 May 2022 16:53:57 +0000 (12:53 -0400)
The fact that the uid/gid/mode are big endian bit me when I was
trying to parse this "by hand" in ostree-rs-ext.

Let's add a footgun-free API for this.

(And yeah, we should probably do the same for the other variant types)

rust-bindings/rust/src/core.rs
rust-bindings/rust/src/repo.rs
rust-bindings/rust/tests/repo/mod.rs

index 616e11232fca32a83dbe26486ead0ce6e658c764..5e294b1b65f24726a5e50b4a28e099ef67fc7db6 100644 (file)
@@ -19,3 +19,30 @@ pub type TreeVariantType = (Vec<(String, Vec<u8>)>, Vec<(String, Vec<u8>, Vec<u8
 
 /// The type of a directory metadata object: `(uuua(ayay))`
 pub type DirmetaVariantType = (u32, u32, u32, Vec<(Vec<u8>, Vec<u8>)>);
+
+/// Parsed representation of directory metadata.
+pub struct DirMetaParsed {
+    /// The user ID.
+    pub uid: u32,
+    /// The group ID.
+    pub gid: u32,
+    /// The Unix mode, including file type flag.
+    pub mode: u32,
+    /// Extended attributes.
+    pub xattrs: Vec<(Vec<u8>, Vec<u8>)>,
+}
+
+impl DirMetaParsed {
+    /// Parse a directory metadata variant; must be of type `(uuua(ayay))`.
+    pub fn from_variant(
+        v: &glib::Variant,
+    ) -> Result<DirMetaParsed, glib::variant::VariantTypeMismatchError> {
+        let (uid, gid, mode, xattrs) = v.try_get::<crate::DirmetaVariantType>()?;
+        Ok(DirMetaParsed {
+            uid: u32::from_be(uid),
+            gid: u32::from_be(gid),
+            mode: u32::from_be(mode),
+            xattrs,
+        })
+    }
+}
index 5329b893653886c5b406659b7e836648217d8c0f..688956927c1d5511f2bea9300c352c792ef99491 100644 (file)
@@ -441,4 +441,13 @@ impl Repo {
             );
         }))
     }
+
+    /// Load and parse directory metadata.
+    /// In particular, uid/gid/mode are stored in big-endian format; this function
+    /// converts them to host native endianness.
+    pub fn read_dirmeta(&self, checksum: &str) -> Result<crate::DirMetaParsed, glib::Error> {
+        let v = self.load_variant(crate::ObjectType::DirMeta, checksum)?;
+        // Safety: We know the variant type will match since we just passed it above
+        Ok(crate::DirMetaParsed::from_variant(&v).unwrap())
+    }
 }
index f56e390e89a575bd56b4a22679d3b8b3b8d8d333..007a0d83383da8ffb48e13945ca0890dfcb53174 100644 (file)
@@ -27,7 +27,7 @@ fn should_commit_content_to_repo_and_list_refs_again() {
 }
 
 #[test]
-fn should_traverse_commit() {
+fn repo_traverse_and_read() {
     let test_repo = TestRepo::new();
     let checksum = test_repo.test_commit("test");
 
@@ -58,6 +58,13 @@ fn should_traverse_commit() {
         ),
         objects
     );
+
+    let dirmeta = test_repo
+        .repo
+        .read_dirmeta("ad49a0f4e3bc165361b6d17e8a865d479b373ee67d89ac6f0ce871f27da1be6d")
+        .unwrap();
+    // Right now, the uid/gid are actually that of the test runner
+    assert_eq!(dirmeta.mode, 0o40750);
 }
 
 #[test]